#include "network.h"

// 标识配置
char* TAG[4] = {"tcp_client", "tcp_server", "udp_client", "udp_server"};

// 网络结构体
struct sockaddr_in tcpSockAddr;
struct sockaddr_in tcpSockCliAddr;
struct sockaddr_in udpSockAddr;
struct sockaddr_in udpSockCliAddr;


/*********************************************************************
* TCP客户端或服务端初始化
* 参数: IP: 客户端必须参数IP值, PORT: 两个模式均需要,  
* mode: TCPCLIMODE, TCP客户端(发送数据), TCPSERMODE, TCP服务端(接收数据)
* 注意: TCPSERMODE 模式下, 返回的套接字需要使用accept()进行连接
* 返回值: -1: 表示链接失败, 成功返回套接字
**********************************************************************/
int TCP_Init(char* IP, int* PORT, int mode)
{
    if(mode < 0 || mode > 1){
        printf("TCP mode selection error\n");
        return -1;
    }

    // TCP 套接字创建 
    int tcpSock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    if(tcpSock < 0){
        printf("Socket creation failed\n");
        close(tcpSock);
    }

    // TCP 客户端模式
    if(mode == TCPCLIMODE){
        if(IP == NULL || PORT == NULL){
            if(IP == NULL){
                ESP_LOGI(TAG[mode], " ip is NULL");
            }else{
                ESP_LOGI(TAG[mode], " port is NULL");
            }
            close(tcpSock);
            return -1;
        }
        memset(&tcpSockAddr, 0, sizeof(struct sockaddr_in)); // 结构体清零
        // 设置IP地址和端口号
        tcpSockAddr.sin_family = AF_INET;
        tcpSockAddr.sin_addr.s_addr = inet_addr(IP);	
        tcpSockAddr.sin_port = htons(*PORT);   // 小端模式转为大端模式
        socklen_t tcpSockAddrLen = sizeof(tcpSockAddr);

        //printf("ip:%s, port:%d\n", IP, *PORT);
        
        int err = connect(tcpSock, (struct sockaddr*)&tcpSockAddr, tcpSockAddrLen);
        if(err < 0){
            ESP_LOGI(TAG[mode], " connect failed, err:%d", errno);
            ESP_ERROR_CHECK(ESP_FAIL);
        }else if(err == 0){
            ESP_LOGI(TAG[mode], " connect success");
        }
    }else if(mode == TCPSERMODE){ // TCP 服务端模式, 该模式后返回的套接字需要使用accept()
        if(PORT == NULL){
            ESP_LOGI(TAG[mode], " port is NULL");
            close(tcpSock);
            return -1;
        }
        
        // 设置端口复用
        int reOpt = 1;
        setsockopt(tcpSock, SOL_SOCKET, SO_REUSEADDR, &reOpt, sizeof(reOpt));

        memset(&tcpSockAddr, 0, sizeof(struct sockaddr_in)); // 结构体清零
        // TCP 服务端绑定本地的IP
        tcpSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
        tcpSockAddr.sin_family = AF_INET;
        tcpSockAddr.sin_port = htons(*PORT);  // 小端模式转为大端模式

        
        int err = bind(tcpSock, (struct sockaddr*)&tcpSockAddr, sizeof(tcpSockAddr));
        if(err == 0){
            ESP_LOGI(TAG[mode], " bind success");
        }else if(err < 0){
            ESP_LOGI(TAG[mode], " bind failed, err:%d", errno);
            ESP_ERROR_CHECK(ESP_FAIL); // 绑定失败就重启esp32
        }
        // 进行监听
        err = listen(tcpSock, 5);
        if (err != 0){
            ESP_LOGI("SERVICE", "Listening failed with error code: %d", errno);
            close(tcpSock);
            return -1;
        }else{
            ESP_LOGI("SERVICE", "Successfully monitored");
        }
    }else{ // TCP传入模式标识符错误
        printf("TCP mode identification value passed in error\n");
    }

    return tcpSock;
}

/*********************************************************************
* UDP客户端或服务端初始化
* 参数: IP: 客户端必须参数IP值, PORT: 两个模式均需要,  
* mode: UDPCLIMODE, TCP客户端(发送数据), UDPSERMODE, TCP服务端(接收数据)
* 返回值: -1: 表示链接失败, 成功返回套接字
**********************************************************************/
int UDP_Init(char* IP, int* PORT, int mode)
{
     if(mode < 2 || mode > 3){
        printf("UDP mode selection error\n");
        return -1;
    }

    // UDP 套接字创建 
    int udpSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    if(udpSock < 0){
        printf("Socket creation failed\n");
        close(udpSock);
    }

    if(mode == UDPCLIMODE) { // UDP客户端模式
        if(IP == NULL || PORT == NULL){
            if(IP == NULL){
                ESP_LOGI(TAG[mode], " ip is NULL");
            }else{
                ESP_LOGI(TAG[mode], " port is NULL");
            }
            close(udpSock);
            return -1;
        }

        memset(&udpSockAddr, 0, sizeof(struct sockaddr_in)); // 结构体清零
        // 设置IP地址和端口号
        udpSockAddr.sin_addr.s_addr = inet_addr(IP);	
        udpSockAddr.sin_family = AF_INET;
        udpSockAddr.sin_port = htons(*PORT);    // 小端模式转为大端模式

    }else if(mode == UDPSERMODE){
       if(PORT == NULL){
            ESP_LOGI(TAG[mode], " port is NULL");
            close(udpSock);
            return -1;
        }

        // 设置端口复用
        int reOpt = 1;
        setsockopt(udpSock, SOL_SOCKET, SO_REUSEADDR, &reOpt, sizeof(reOpt));

        memset(&udpSockAddr, 0, sizeof(struct sockaddr_in)); // 结构体清零
        // UDP 服务端绑定本地的IP
        udpSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
        udpSockAddr.sin_family = AF_INET;
        udpSockAddr.sin_port = htons(*PORT);  // 小端模式转为大端模式

        int err = bind(udpSock, (struct sockaddr*)&udpSockAddr, sizeof(udpSockAddr));
        if(err == 0){
            ESP_LOGI(TAG[mode], " bind success");
        }else if(err < 0){
            ESP_LOGI(TAG[mode], " bind failed, err:%d", errno);
            ESP_ERROR_CHECK(ESP_FAIL);
        }
    }

    return udpSock;
}














